﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace RFEngine.Audio
{
    /// <summary>
    /// Delegate used for SongEvents.
    /// </summary>
    /// <param name="song">The Song that called the delegate</param>
    public delegate void SongEvent(Song song);

    public sealed class Song
    {
        #region Fields

        private bool _Destroyed;
        private string _Name;    
    
        private int _Length;
        private int _PlayPosition = 0;

        private float _Volume = 1.0f;
        private float _PlaySpeed = 1.0f;        

        private AudioState _AudioState;
        private TrackCollection _Tracks = new TrackCollection();

        #region Events and Delegates

        /// <summary>
        /// An Event that is triggered once this song has completed.
        /// </summary>
        public event SongEvent SongCompletedEvent;

        #endregion

        #endregion

        #region Properties

        /// <summary>
        /// Gets the name of this object.
        /// </summary>
        public string Name
        {
            get { return _Name; }           
        }

        /// <summary>
        /// Gets the length in miliseconds of the longest track.
        /// </summary>
        public int Length
        {
            get { return _Length; }
        }

        /// <summary>
        /// Gets the Sets current play position of this song.
        /// </summary>
        public int PlayPosition
        {
            get { return _PlayPosition; }
            set 
            { 
                _PlayPosition = value;
                _Tracks.ForEach((track) => track.PlayPosition = _PlayPosition);
            }
        }

        /// <summary>
        /// Gets or Sets the speed at which this song plays.
        /// </summary>
        public float PlaySpeed
        {
            get { return _PlaySpeed; }
            set 
            { 
                _PlaySpeed = value;
                _Tracks.ForEach((track) => track.PlaySpeed = _PlaySpeed);
            }
        }

        /// <summary>
        /// Gets the state of this object.
        /// </summary>
        public AudioState AudioState
        {
            get { return _AudioState; }
        }

        /// <summary>
        /// Gets the Collection of tracks associated with this object.
        /// </summary>
        public TrackCollection Tracks
        {
            get { return _Tracks; }
        }

        #endregion

        #region Methods

        #region Constructor(s)

        /// <summary>
        /// Constructs a new instance of this object.
        /// </summary>
        /// <param name="songName">The name of this object.</param>
        internal Song(string name)
        {
            _Name = name;
            SongCompletedEvent += new SongEvent(OnSongComplete);
        }        

        #endregion

        /// <summary>
        /// Method for adding a new track instance to this Song.
        /// </summary>
        /// <param name="fileLocation">A string value representing the location of the _Sound</param>
        /// <param name="trackName">The track name to use for this Track.</param>
        /// <returns>Returns a new instance of the track object.</returns>
        public Track AddTrack(string fileLocation, string trackName)
        {
            _Tracks.Add(new Track(this, fileLocation, trackName));
            return _Tracks.Last();
        }

        /// <summary>
        /// Method for pausing all playing tracks.
        /// </summary>
        public void Pause()
        {
            if (_AudioState != AudioState.Playing)
            {
                _Tracks.ForEach((track) => track.Pause());
                _AudioState = AudioState.Paused;
            }
        }

        /// <summary>
        /// Method for playing any non-playing tracks.
        /// </summary>
        public void Play()
        {
            if (_AudioState != AudioState.Playing && _AudioState != AudioState.Paused)
            {
                _Tracks.ForEach((track) => track.Play());
                _AudioState = AudioState.Playing;
            }
            else if (_AudioState == AudioState.Paused)
                Resume();
        }

        /// <summary>
        /// Method for resuming any paused tracks.
        /// </summary>
        public void Resume()
        {
            if (_AudioState == AudioState.Paused)
            {
                _Tracks.ForEach((track) => track.Resume());
                _AudioState = AudioState.Playing;
            }
        }

        /// <summary>
        /// Method for stopping all playing tracks.
        /// </summary>
        public void Stop()
        {
            if (_AudioState == AudioState.Playing)
            {
                _Tracks.ForEach((track) => track.Stop());
                _AudioState = AudioState.Stopped;
            }
        }

        /// <summary>
        /// Method for Removing a Track from this Song.
        /// </summary>
        /// <param name="item">The track item to remove from this song.</param>
        public void RemoveTrack(Track item)
        {
            if (_Tracks.Remove(item))
                item.Destroy(0);
        }

        /// <summary>
        /// Method for getting a track by the track name.
        /// </summary>
        /// <param name="trackName">The name of the track to try and retrieve.</param>
        /// <param name="track">The track to set if found.</param>
        /// <returns>Returns true if item existed, otherwise false.</returns>
        public bool GetTrackByName(string trackName, out Track track)
        {
            track = null;

            for (int i = 0; i < _Tracks.Count; i++)
                if (_Tracks[i].Name == trackName)
                {
                    track = _Tracks[i];
                    return true;
                }

            return false;
        }

        #region Private Methods

        private void OnSongComplete(Song song) { }

        #endregion

        #region Internal Methods

        /// <summary>
        /// Method for destroying this object.
        /// </summary>
        internal void Destroy()
        {
            if (!_Destroyed)
            {
                 _Tracks.ForEach((track) => track.Destroy(0));
                _Destroyed = true;
            }
        }

        #endregion

        #region Destructor

        ~Song()
        {
            if (!_Destroyed)
                Destroy();
        }

        #endregion

        #endregion
    }
}
